/*

总结：emplace_back 可以避免对象的冗余拷贝/移动。

设容器的元素类型为 T
1. emplace_back 可以完美转发参数，因此如果 T 为自定义类型，
    且其构造函数有多个参数，可以直接 emplace_back(args...) 完美转发
    在容器内部直接构造。
2. 使用 push_back 会造成一定程度(通常不会非常大)的浪费，
    因为 push_back 之前你必须要有一个 T 类型的对象
    这个对象通常只是临时的，因此特地把它创建出来多少有些冗余。
3. 原理是[可变参数模板]&[完美转发].

该程序的输出：
```bash
scutech@ubuntu:~/xyl/find_jobs/build$ ./push_back_and_emplace_back 
push_back: 
Kobe 构造
Kobe 拷贝
Jack 构造
Jack 移动
临时对象析构

emplace_back: 
Mary 构造
Curry 构造

end:
Kobe 析构

析构 vector<Person> people: 
Kobe 析构
Jack 析构
Mary 析构
Curry 析构
```
*/

#include <iostream>
#include <vector>
#include <string>

using namespace std;

class Person
{
public:
    Person(const string& name, int age): name_(name), age_(age)
    {
        cout << name_ << " 构造\n";
    }

    Person(const Person& other): name_(other.name_), age_(other.age_)
    {
        cout << name_ << " 拷贝\n";
    }

    Person(Person&& other) noexcept: name_(move(other.name_)), age_(other.age_)
    {
        cout << name_ << " 移动\n";
    }

    ~Person()
    {
        if (name_.empty()) {
            cout << "临时对象析构\n";
        } else {
            cout << name_ << " 析构\n";
        }
    }

private:
    string name_;
    int age_;
};

vector<Person> people;

void test()
{
    // 注：这里先提前扩容，避免影响实验输出。
    // 因为扩容时，会移动元素到内存其他位置，调用移动构造。
    people.reserve(100);

    // people.push_back("Mike", 19);    // Compile Error
    // people.emplace_back("Jack", 20); // OK

    cout << "push_back: \n";

    Person man("Kobe", 40);               // 构造
    people.push_back(man);                // 拷贝
    people.push_back(Person("Jack", 30)); // 构造临时对象 + 移动

    cout << "\nemplace_back: \n";

    people.emplace_back("Mary", 16);  // 构造
    people.emplace_back("Curry", 30); // 构造

    cout << "\nend:\n";
}
int main()
{
    test();

    cout << "\n析构 vector<Person> people: \n";
    return 0;
}